home *** CD-ROM | disk | FTP | other *** search
/ PC World 2008 September / PCWorld_2008-09_cd.bin / v cisle / sadanastroju / lightning-0.8-tb-win.xpi / chrome / calendar.jar / content / calendar / calendar-item-editing.js < prev    next >
Text File  |  2008-03-03  |  17KB  |  457 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is Oracle Corporation code.
  15.  *
  16.  * The Initial Developer of the Original Code is Oracle Corporation
  17.  * Portions created by the Initial Developer are Copyright (C) 2005
  18.  * the Initial Developer. All Rights Reserved.
  19.  *
  20.  * Contributor(s):
  21.  *   Stuart Parmenter <stuart.parmenter@oracle.com>
  22.  *   Robin Edrenius <robin.edrenius@gmail.com>
  23.  *   Philipp Kewisch <mozilla@kewis.ch>
  24.  *
  25.  * Alternatively, the contents of this file may be used under the terms of
  26.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  27.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  28.  * in which case the provisions of the GPL or the LGPL are applicable instead
  29.  * of those above. If you wish to allow use of your version of this file only
  30.  * under the terms of either the GPL or the LGPL, and not to allow others to
  31.  * use your version of this file under the terms of the MPL, indicate your
  32.  * decision by deleting the provisions above and replace them with the notice
  33.  * and other provisions required by the GPL or the LGPL. If you do not delete
  34.  * the provisions above, a recipient may use your version of this file under
  35.  * the terms of any one of the MPL, the GPL or the LGPL.
  36.  *
  37.  * ***** END LICENSE BLOCK ***** */
  38.  
  39. function opCompleteListener(aOriginalItem, aOuterListener) {
  40.     this.mOriginalItem = aOriginalItem;
  41.     this.mOuterListener = aOuterListener;
  42. }
  43.  
  44. opCompleteListener.prototype = {
  45.     mOriginalItem: null,
  46.     mOuterListener: null,
  47.  
  48.     onOperationComplete: function oCL_onOperationComplete(aCalendar, aStatus, aOpType, aId, aItem) {
  49.         if (Components.isSuccessCode(aStatus)) {
  50.             checkForAttendees(aItem, this.mOriginalItem);
  51.         }
  52.         if (this.mOuterListener) {
  53.             this.mOuterListener.onOperationComplete.apply(this.mOuterListener,
  54.                                                           arguments);
  55.         }
  56.     },
  57.  
  58.     onGetItem: function oCL_onGetResult(aCalendar, aStatus, aItemType, aDetail, aCount, aItems) {
  59.  
  60.     }
  61. };
  62.  
  63. /* all params are optional */
  64. function createEventWithDialog(calendar, startDate, endDate, summary, event) {
  65.     const kDefaultTimezone = calendarDefaultTimezone();
  66.  
  67.     var onNewEvent = function(item, calendar, originalItem, listener) {
  68.         var innerListener = new opCompleteListener(originalItem, listener);
  69.         if (item.id) {
  70.             // If the item already has an id, then this is the result of
  71.             // saving the item without closing, and then saving again.
  72.             if (!originalItem.calendar || originalItem.calendar.id == calendar.id) {
  73.                 doTransaction('modify', item, calendar, originalItem, innerListener);
  74.             } else {
  75.                 doTransaction('move', item, calendar, originalItem, innerListener);
  76.             }
  77.         } else {
  78.             // Otherwise, this is an addition
  79.             doTransaction('add', item, calendar, null, innerListener);
  80.         }
  81.     };
  82.  
  83.     if (event) {
  84.         // If the event should be created from a template, then make sure to
  85.         // remove the id so that the item obtains a new id when doing the
  86.         // transaction
  87.         if (event.id) {
  88.             event = event.clone();
  89.             event.id = null;
  90.         }
  91.  
  92.     } else {
  93.         event = createEvent();
  94.  
  95.         if (!startDate) {
  96.             // Have we shown the calendar view yet? (Lightning)
  97.             if (currentView().initialized) {
  98.                 startDate = currentView().selectedDay.clone();
  99.             } else {
  100.                 startDate = jsDateToDateTime(new Date()).getInTimezone(kDefaultTimezone);
  101.             }
  102.             startDate.isDate = true;
  103.         }
  104.  
  105.         if (startDate.isDate) {
  106.             if (!startDate.isMutable) {
  107.                 startDate = startDate.clone();
  108.             }
  109.             startDate.isDate = false;
  110.             // The time for the event should default to the next full hour
  111.             startDate.hour = now().hour + 1;
  112.             startDate.minute = 0;
  113.             startDate.second = 0;
  114.         }
  115.  
  116.         if (!endDate) {
  117.             endDate = startDate.clone();
  118.             endDate.minute += getPrefSafe("calendar.event.defaultlength", 60);
  119.         }
  120.  
  121.         event.startDate = startDate.clone();
  122.         event.endDate = endDate.clone();
  123.  
  124.         event.calendar = calendar || getSelectedCalendar();
  125.  
  126.         if (summary)
  127.             event.title = summary;
  128.  
  129.         setDefaultAlarmValues(event);
  130.     }
  131.     openEventDialog(event, calendar, "new", onNewEvent, null);
  132. }
  133.  
  134. function createTodoWithDialog(calendar, dueDate, summary, todo) {
  135.     const kDefaultTimezone = calendarDefaultTimezone();
  136.  
  137.     var onNewItem = function(item, calendar, originalItem, listener) {
  138.         var innerListener = new opCompleteListener(originalItem, listener);
  139.         if (item.id) {
  140.             // If the item already has an id, then this is the result of
  141.             // saving the item without closing, and then saving again.
  142.             if (!originalItem.calendar || originalItem.calendar.id == calendar.id) {
  143.                 doTransaction('modify', item, calendar, originalItem, innerListener);
  144.             } else {
  145.                 doTransaction('move', item, calendar, originalItem, innerListener);
  146.             }
  147.         } else {
  148.             // Otherwise, this is an addition
  149.             doTransaction('add', item, calendar, null, innerListener);
  150.         }
  151.     }
  152.  
  153.     if (todo) {
  154.         // If the too should be created from a template, then make sure to
  155.         // remove the id so that the item obtains a new id when doing the
  156.         // transaction
  157.         if (todo.id) {
  158.             todo = todo.clone();
  159.             todo.id = null;
  160.         }
  161.     } else {
  162.         todo = createTodo();
  163.         todo.calendar = calendar || getSelectedCalendar();
  164.  
  165.         if (summary)
  166.             todo.title = summary;
  167.  
  168.         if (dueDate)
  169.             todo.dueDate = dueDate;
  170.  
  171.         setDefaultAlarmValues(todo);
  172.     }
  173.  
  174.     openEventDialog(todo, calendar, "new", onNewItem, null);
  175. }
  176.  
  177.  
  178. function modifyEventWithDialog(item, job) {
  179.     var onModifyItem = function(item, calendar, originalItem, listener) {
  180.         var innerListener = new opCompleteListener(originalItem, listener);
  181.  
  182.         if (!originalItem.calendar || originalItem.calendar.id == calendar.id) {
  183.             doTransaction('modify', item, calendar, originalItem, innerListener);
  184.         } else {
  185.             doTransaction('move', item, calendar, originalItem, innerListener);
  186.         }
  187.     };
  188.  
  189.     if (item) {
  190.         openEventDialog(item, item.calendar, "modify", onModifyItem, job);
  191.     }
  192. }
  193.  
  194. function openEventDialog(calendarItem, calendar, mode, callback, job) {
  195.     // Set up some defaults
  196.     mode = mode || "new";
  197.     calendar = calendar || getSelectedCalendar();
  198.     var calendars = getCalendarManager().getCalendars({});
  199.     calendars = calendars.filter(isCalendarWritable);
  200.  
  201.     var isItemSupported;
  202.     if (isToDo(calendarItem)) {
  203.         isItemSupported = function isTodoSupported(cal) {
  204.             return (cal.getProperty("capabilities.tasks.supported") !== false);
  205.         };
  206.     } else if (isEvent(calendarItem)) {
  207.         isItemSupported = function isEventSupported(cal) {
  208.             return (cal.getProperty("capabilities.events.supported") !== false);
  209.         };
  210.     }
  211.  
  212.     // Filter out calendars that don't support the given calendar item
  213.     calendars = calendars.filter(isItemSupported);
  214.  
  215.     if (mode == "new" && calendars.length < 1 &&
  216.         (!isCalendarWritable(calendar) || !isItemSupported(calendar))) {
  217.         // There are no writable calendars or no calendar supports the given
  218.         // item. Don't show the dialog.
  219.         return;
  220.     } else if (mode == "new" &&
  221.                (!isCalendarWritable(calendar) || !isItemSupported(calendar))) {
  222.         // Pick the first calendar that supports the item and is writable
  223.         calendar = calendars[0];
  224.         if (calendarItem) {
  225.             // XXX The dialog currently uses the items calendar as a first
  226.             // choice. Since we are shortly before a release to keep regression
  227.             // risk low, explicitly set the item's calendar here.
  228.             calendarItem.calendar = calendars[0];
  229.         }
  230.     }
  231.  
  232.     // Setup the window arguments
  233.     var args = new Object();
  234.     args.calendarEvent = calendarItem;
  235.     args.calendar = calendar;
  236.     args.mode = mode;
  237.     args.onOk = callback;
  238.     args.job = job;
  239.  
  240.     // this will be called if file->new has been selected from within the dialog
  241.     args.onNewEvent = function(calendar) {
  242.         createEventWithDialog(calendar, null, null);
  243.     }
  244.  
  245.     // the dialog will reset this to auto when it is done loading.
  246.     window.setCursor("wait");
  247.  
  248.     // ask the provide if this item is an invitation. if this is the case
  249.     // we'll open the summary dialog since the user is not allowed to change
  250.     // the details of the item.
  251.     var isInvitation = false;
  252.     try {
  253.         isInvitation = calendar.isInvitation(calendarItem);
  254.     }
  255.     catch(e) {}
  256.  
  257.     // open the dialog modeless
  258.     var url = "chrome://calendar/content/sun-calendar-event-dialog.xul";
  259.     if ((mode != "new" && isInvitation) || !isCalendarWritable(calendar)) {
  260.         url = "chrome://calendar/content/calendar-summary-dialog.xul";
  261.     }
  262.     openDialog(url, "_blank", "chrome,titlebar,resizable", args);
  263. }
  264.  
  265. // When editing a single instance of a recurring event, we need to figure out
  266. // whether the user wants to edit all instances, or just this one.  This
  267. // function prompts this question (if the item is actually an instance of a
  268. // recurring event) and returns the appropriate item that should be modified.
  269. // Returns null if the prompt was cancelled.
  270. function getOccurrenceOrParent(occurrence) {
  271.     // Check if this actually is an instance of a recurring event
  272.     if (occurrence == occurrence.parentItem) {
  273.         return occurrence;
  274.     }
  275.  
  276.     // if the user wants to edit an occurrence which is already
  277.     // an exception, always edit this single item.
  278.     var parentItem = occurrence.parentItem;
  279.     var rec = parentItem.recurrenceInfo;
  280.     if (rec) {
  281.         var exceptions = rec.getExceptionIds({});
  282.         if (exceptions.some(function (exid) {
  283.                                 return exid.compare(occurrence.recurrenceId) == 0;
  284.                             })) {
  285.             return occurrence;
  286.         }
  287.     }
  288.  
  289.     var promptService = 
  290.              Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
  291.                        .getService(Components.interfaces.nsIPromptService);
  292.  
  293.     var promptTitle = calGetString("calendar", "editRecurTitle");
  294.     var promptMessage = calGetString("calendar", "editRecurMessage");
  295.     var buttonLabel1 = calGetString("calendar", "editRecurAll");
  296.     var buttonLabel2 = calGetString("calendar", "editRecurSingle");
  297.  
  298.     var flags = promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0 +
  299.                 promptService.BUTTON_TITLE_CANCEL * promptService.BUTTON_POS_1 +
  300.                 promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_2;
  301.  
  302.     var choice = promptService.confirmEx(null, promptTitle, promptMessage, flags,
  303.                                          buttonLabel1,null , buttonLabel2, null, {});
  304.     switch(choice) {
  305.         case 0: return occurrence.parentItem;
  306.         case 2: return occurrence;
  307.         default: return null;
  308.     }
  309. }
  310.  
  311. /**
  312.  * Read default alarm settings from user preferences and apply them to
  313.  * the event/todo passed in.
  314.  *
  315.  * @param aItem   The event or todo the settings should be applied to.
  316.  */
  317. function setDefaultAlarmValues(aItem)
  318. {
  319.     var prefService = Components.classes["@mozilla.org/preferences-service;1"]
  320.                                 .getService(Components.interfaces.nsIPrefService);
  321.     var alarmsBranch = prefService.getBranch("calendar.alarms.");
  322.  
  323.     if (isEvent(aItem)) {
  324.         try {
  325.             if (alarmsBranch.getIntPref("onforevents") == 1) {
  326.                 var alarmOffset = Components.classes["@mozilla.org/calendar/duration;1"]
  327.                                             .createInstance(Components.interfaces.calIDuration);
  328.                 var units = alarmsBranch.getCharPref("eventalarmunit");
  329.                 alarmOffset[units] = alarmsBranch.getIntPref("eventalarmlen");
  330.                 alarmOffset.isNegative = true;
  331.                 aItem.alarmOffset = alarmOffset;
  332.                 aItem.alarmRelated = Components.interfaces.calIItemBase.ALARM_RELATED_START;
  333.             }
  334.         } catch (ex) {
  335.             Components.utils.reportError(
  336.                 "Failed to apply default alarm settings to event: " + ex);
  337.         }
  338.     } else if (isToDo(aItem)) {
  339.         try {
  340.             if (alarmsBranch.getIntPref("onfortodos") == 1) {
  341.                 // You can't have an alarm if the entryDate doesn't exist.
  342.                 if (!aItem.entryDate) {
  343.                     aItem.entryDate = getSelectedDay() &&
  344.                                       getSelectedDay().clone() || now();
  345.                 }
  346.                 var alarmOffset = Components.classes["@mozilla.org/calendar/duration;1"]
  347.                                             .createInstance(Components.interfaces.calIDuration);
  348.                 var units = alarmsBranch.getCharPref("todoalarmunit");
  349.                 alarmOffset[units] = alarmsBranch.getIntPref("todoalarmlen");
  350.                 alarmOffset.isNegative = true;
  351.                 aItem.alarmOffset = alarmOffset;
  352.                 aItem.alarmRelated = Components.interfaces.calIItemBase.ALARM_RELATED_START;
  353.             }
  354.         } catch (ex) {
  355.             Components.utils.reportError(
  356.                 "Failed to apply default alarm settings to task: " + ex);
  357.         }
  358.     }
  359. }
  360.  
  361. // Undo/Redo code
  362. function getTransactionMgr() {
  363.     return Components.classes["@mozilla.org/calendar/transactionmanager;1"]
  364.                      .getService(Components.interfaces.calITransactionManager);
  365. }
  366.  
  367. function doTransaction(aAction, aItem, aCalendar, aOldItem, aListener) {
  368.     getTransactionMgr().createAndCommitTxn(aAction,
  369.                                            aItem,
  370.                                            aCalendar,
  371.                                            aOldItem,
  372.                                            aListener);
  373.     updateUndoRedoMenu();
  374. }
  375.  
  376. function undo() {
  377.     getTransactionMgr().undo();
  378.     updateUndoRedoMenu();
  379. }
  380.  
  381. function redo() {
  382.     getTransactionMgr().redo();
  383.     updateUndoRedoMenu();
  384. }
  385.  
  386. function startBatchTransaction() {
  387.     getTransactionMgr().beginBatch();
  388. }
  389. function endBatchTransaction() {
  390.     getTransactionMgr().endBatch();
  391.     updateUndoRedoMenu();
  392. }
  393.  
  394. function canUndo() {
  395.     return getTransactionMgr().canUndo();
  396. }
  397. function canRedo() {
  398.     return getTransactionMgr().canRedo();
  399. }
  400.  
  401. /**
  402.  * checkForAttendees
  403.  * Checks to see if the attendees were added or changed between the original
  404.  * and new item.  If there is a change, it launches the calIITipTransport
  405.  * service and sends the invitations
  406.  */
  407. function checkForAttendees(aItem, aOriginalItem)
  408. {
  409.     // iTIP is only supported in Lightning right now
  410.     if (isSunbird()) {
  411.         return;
  412.     }
  413.  
  414.     // Only send invitations for providers which need it.
  415.     if (!aItem.calendar.sendItipInvitations) {
  416.         return;
  417.     }
  418.  
  419.     // Only send invitations if the user checked the checkbox.
  420.     if (!aItem.hasProperty("X-MOZ-SEND-INVITATIONS")) {
  421.         return;
  422.     } else if (aItem.getProperty("X-MOZ-SEND-INVITATIONS") != "TRUE") {
  423.         return;
  424.     }
  425.  
  426.     var sendInvite = false;
  427.     var itemAtt = aItem.getAttendees({});
  428.  
  429.     if (itemAtt.length > 0) {
  430.         var originalAtt = aOriginalItem.getAttendees({});
  431.  
  432.         if ( (originalAtt.length > 0) &&
  433.              (originalAtt.length == itemAtt.length) )
  434.         {
  435.             for (var i=0; i < itemAtt.length; i++) {
  436.                 if (originalAtt[i].id != itemAtt[i].id) {
  437.                     sendInvite = true;
  438.                     break;
  439.                 }
  440.             }
  441.         } else {
  442.             // We have attendees on item, not on original, attendees were
  443.             // added.
  444.             sendInvite = true;
  445.         }
  446.     }
  447.  
  448.     // Check to see if some part of the item was updated, if so, re-send invites
  449.     if (!sendInvite && (aItem.generation != aOriginalItem.generation))
  450.         sendInvite = true;
  451.  
  452.     if (sendInvite) {
  453.         // Now use calUtils.js to send these
  454.         sendItipInvitation(aItem);
  455.     }
  456. }
  457.